﻿using System;
using System.Collections;
using System.Data;
using System.Diagnostics;
using System.Reflection;
using System.ServiceModel;
using System.Web;
using System.Web.Caching;
using Contracts.ClientTools;
using Contracts.ServiceContracts.ClientCache;
using Types.Constants;
using Types.Enumerations;


namespace Contracts.Caching
{
	internal class CustomInMemoryCache : ICacheProvider
	{
		public static CustomInMemoryCache GetProviderInstance
		{
			get { return new CustomInMemoryCache(); }
		}

		private static Hashtable ht;

		static CustomInMemoryCache()
		{
			ht = new Hashtable();

			foreach (CacheItem ci in CacheProvider.ProviderSettings.CacheItems)
			{
				ht.Add(ci.CacheItemName, ci);
			}
		}

		private static void InsertCache(string key, CacheItem pCacheItem)
		{
			DataSet ds = null;

			if (pCacheItem.CacheType == EnumCacheType.ClientCache)
			{
				ds = ServiceProxyCreator<IClientCache, DataSet>.CallService(proxy =>
				{
					return proxy.GetCacheItemWithRefresh(key);
				}
				);

			}
			else
			{

				Assembly asm = Assembly.GetAssembly(Type.GetType("Services.BusinessObjects.ClientCache.ClientCacheBS, Services"));

				if (pCacheItem.InvokeDirectly)
				{

					try
					{
						Type bsType = asm.GetType("Services.BusinessObjects.ClientCache.BSServiceCacheHelper");
						object mBSServiceCacheHelper = bsType.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
						ds = (DataSet)bsType.InvokeMember("HelpTableCache", BindingFlags.InvokeMethod, null, mBSServiceCacheHelper
							, new object[] { pCacheItem.TypedDsName });
					}
					catch (TargetInvocationException tEx)
					{
						EventLog.WriteEntry(CommonConstants.EventLogSource.Application, tEx.ToString(), EventLogEntryType.Error, CacheEventLogId.CacheError);
						if (tEx.InnerException != null)
						{
							throw new FaultException(new FaultReason(tEx.InnerException.Message));
						}
						else
						{
							throw new FaultException(new FaultReason(tEx.Message));
						}
					}
					catch (Exception exc)
					{
						EventLog.WriteEntry(CommonConstants.EventLogSource.Application, exc.Message, EventLogEntryType.Error, CacheEventLogId.CacheError);
						throw new FaultException(new FaultReason(exc.Message));
					}

				}
				else
				{
					try
					{
						foreach (Type t in asm.GetTypes())
						{
							if (t.GetInterface(pCacheItem.Interfacename.FullName) != null)
							{
								object mInstance = t.InvokeMember(null, BindingFlags.CreateInstance, null, null, null);
								ds = (DataSet)pCacheItem.Interfacename.GetMethod(pCacheItem.MethodName).Invoke(mInstance, null);
								break;
							}
						}
					}
					catch (TargetInvocationException tEx)
					{
						EventLog.WriteEntry(CommonConstants.EventLogSource.Application, tEx.ToString(), EventLogEntryType.Error, CacheEventLogId.CacheError);
						if (tEx.InnerException != null)
						{
							throw new FaultException(new FaultReason(tEx.InnerException.Message));
						}
						else
						{
							throw new FaultException(new FaultReason(tEx.Message));
						}
					}
					catch (Exception ex)
					{
						EventLog.WriteEntry(CommonConstants.EventLogSource.Application, ex.Message, EventLogEntryType.Error, CacheEventLogId.CacheError);
						throw new FaultException(new FaultReason(ex.Message));
					}
				}
			}

			// if expirationInMinutes <= 0 then does not expire .... 
			if (pCacheItem.MinutesToExpire <= 0)
			{
				HttpRuntime.Cache.Insert(key,
					ds,
					null,
					Cache.NoAbsoluteExpiration,
					Cache.NoSlidingExpiration,
					CacheItemPriority.NotRemovable,
					null);
				EventLog.WriteEntry(CommonConstants.EventLogSource.Application, "Cache Insert Sonsuz: " + key, EventLogEntryType.Information, CacheEventLogId.CacheFilled);
			}
			else
			{
				HttpRuntime.Cache.Insert(key,
					ds,
					null,
					DateTime.Now.AddMinutes(pCacheItem.MinutesToExpire),
					Cache.NoSlidingExpiration,
					CacheItemPriority.NotRemovable,
					null);
				EventLog.WriteEntry(CommonConstants.EventLogSource.Application, "Cache Insert Sureli: " + key, EventLogEntryType.Information, CacheEventLogId.CacheFilled);
			}

			ds = null;
		}

		public static DataSet StaticGetCacheItem(string key)
		{
			DataSet ds = (DataSet)HttpRuntime.Cache[key];

			if (ds != null)
			{
				Debug.WriteLine("Cache hit " + key);
				return ds;
			}

			if ((ds == null) && (ht.ContainsKey(key)))
			{
				lock ((CacheItem)ht[key])
				{

					ds = (DataSet)HttpRuntime.Cache[key];

					if (ds != null)
					{
						Debug.WriteLine("Cache hit " + key);
						return ds;
					}
					Debug.WriteLine("Cache miss " + key);
					CacheItem ci = (CacheItem)ht[key];
					InsertCache(key, ci);
					return (DataSet)HttpRuntime.Cache[key];
				}
			}

			return null;
		}

		public static void StaticRefreshCacheItem(string key)
		{
			HttpRuntime.Cache.Remove(key);
		}

		public static bool StaticHasCacheItem(string key)
		{
			if (((DataSet)HttpRuntime.Cache[key]) != null) return true; else return false;
		}

		#region ICacheProvider Members

		public T GetCacheItem<T>(string key) where T : DataSet
		{
			return (T)CustomInMemoryCache.StaticGetCacheItem(key);
		}

		public DataSet GetCacheItem(string key)
		{
			return CustomInMemoryCache.StaticGetCacheItem(key);
		}

		public void RefreshCacheItem(string key)
		{
			CustomInMemoryCache.StaticRefreshCacheItem(key);
		}

		public void RemoveCacheItem(string key)
		{
			throw new NotImplementedException("This method cannot be called for this cache provider!");
		}

		public bool HasCacheItem(string key)
		{
			return StaticHasCacheItem(key);
		}

		public DataSet GetCacheItemIfNewer(string key)
		{
			throw new NotImplementedException();
		}


		#endregion
	}
}
